package org.framework.lazy.cloud.network.heartbeat.client.netty.proxy.socks.socket;


import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.framework.lazy.cloud.network.heartbeat.client.config.NettyClientProperties;
import org.framework.lazy.cloud.network.heartbeat.client.netty.proxy.socks.filter.NettySocksClientProxyClientRealFilter;
import org.framework.lazy.cloud.network.heartbeat.client.netty.proxy.socks.filter.NettySocksClientProxyClientTransferFilter;
import org.framework.lazy.cloud.network.heartbeat.common.NettyRealIdContext;
import org.framework.lazy.cloud.network.heartbeat.common.adapter.ChannelTypeAdapter;
import org.framework.lazy.cloud.network.heartbeat.common.advanced.HandleChannelTypeAdvanced;
import org.framework.lazy.cloud.network.heartbeat.common.advanced.payload.NettyProxyMsg;
import org.framework.lazy.cloud.network.heartbeat.common.constant.ProxyMessageType;
import org.framework.lazy.cloud.network.heartbeat.common.factory.EventLoopGroupFactory;
import org.framework.lazy.cloud.network.heartbeat.common.utils.ChannelAttributeKeyUtils;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 客户端连接真实服务
 */
@Slf4j
public class NettySocksClientProxyClientRealSocket {


    public static void buildRealServer(String clientId,
                                       String clientTargetIp,
                                       Integer clientTargetPort,
                                       String visitorId,
                                       NettyClientProperties nettyClientProperties,
                                       List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList) {

        try {
            EventLoopGroup eventLoopGroup = EventLoopGroupFactory.createClientWorkGroup();

            Bootstrap bootstrap = new Bootstrap();
            bootstrap
                    .group(eventLoopGroup)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
//                     设置读缓冲区为2M
                    .option(ChannelOption.SO_RCVBUF, 2048 * 1024)
//                     设置写缓冲区为1M
                    .option(ChannelOption.SO_SNDBUF, 1024 * 1024)
                    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000 * 60)//连接超时时间设置为 60 秒
                    .option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(1024 * 1024, 1024 * 1024 * 2))
                    .handler(new NettySocksClientProxyClientRealFilter())
            ;


            bootstrap.connect(clientTargetIp, clientTargetPort).addListener((ChannelFutureListener) future -> {
                if (future.isSuccess()) {
                    // 客户端链接真实服务成功 设置自动读写false 等待访客连接成功后设置成true
                    Channel realChannel = future.channel();
                    realChannel.config().setOption(ChannelOption.AUTO_READ, false);

                    log.info("访客通过 客户端:【{}】,visitorId:{},绑定本地服务,IP:{},端口:{} 新建通道成功", clientId, visitorId, clientTargetIp, clientTargetPort);
                    // 客户端真实通道
                    NettyRealIdContext.pushReal(realChannel, visitorId);
                    // 绑定访客ID到当前真实通道属性
                    ChannelAttributeKeyUtils.buildVisitorId(realChannel, visitorId);
                    ChannelAttributeKeyUtils.buildClientId(realChannel, clientId);

                    // 连接服务端 然后绑定通道
                    // 新建一个通道处理
                    newVisitorConnect2Server(
                            clientId,
                            clientTargetIp,
                            clientTargetPort,
                            visitorId,
                            realChannel,
                            nettyClientProperties,
                            handleChannelTypeAdvancedList
                    );
                } else {
                    log.error("客户：【{}】,无法连接当前网络内的目标IP：【{}】,目标端口:【{}】", clientId, clientTargetIp, clientTargetPort);
                    //  DefaultSocks5CommandResponse commandResponse = new DefaultSocks5CommandResponse(Socks5CommandStatus.FAILURE, socks5AddressType);
                    //  transferChannel.writeAndFlush(commandResponse);
                    //  realChannel.close();
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 创建访客连接服务端
     *
     * @param nettyClientProperties         服务端配置信息
     * @param handleChannelTypeAdvancedList 处理器适配器
     * @throws InterruptedException 异常
     */
    protected static void newVisitorConnect2Server(String clientId,
                                                   String clientTargetIp,
                                                   Integer clientTargetPort,
                                                   String visitorId,
                                                   Channel realChannel,
                                                   NettyClientProperties nettyClientProperties,
                                                   List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList) throws InterruptedException {
        EventLoopGroup eventLoopGroup = EventLoopGroupFactory.createClientWorkGroup();
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(eventLoopGroup)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.SO_KEEPALIVE, true)
                // 设置读缓冲区为2M
                .option(ChannelOption.SO_RCVBUF, 2048 * 1024)
                // 设置写缓冲区为1M
                .option(ChannelOption.SO_SNDBUF, 1024 * 1024)
//                .option(ChannelOption.TCP_NODELAY, false)
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000 * 60)//连接超时时间设置为 60 秒
                .option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(1024 * 1024, 1024 * 1024 * 2))

                .handler(new NettySocksClientProxyClientTransferFilter(new ChannelTypeAdapter(handleChannelTypeAdvancedList)))
        ;

        String inetHost = nettyClientProperties.getInetHost();
        int inetPort = nettyClientProperties.getInetPort();
        // local client id


        // 客户端新建访客通道 连接服务端IP:{},连接服务端端口:{}
        log.info("client creates a new visitor channel to connect to server IP: {}, connecting to server port: {} with visitorId:{} & clientId:{}", inetHost, inetPort, visitorId, clientId);
        ChannelFuture future = bootstrap.connect(inetHost, inetPort);

        future.addListener((ChannelFutureListener) futureListener -> {
            Channel transferChannel = futureListener.channel();
            if (futureListener.isSuccess()) {
                realChannel.config().setOption(ChannelOption.AUTO_READ, true);
                // 通知服务端访客连接成功
                NettyProxyMsg nettyProxyMsg = new NettyProxyMsg();
                nettyProxyMsg.setVisitorId(visitorId);
                nettyProxyMsg.setClientId(clientId);
                nettyProxyMsg.setClientTargetIp(clientTargetIp);
                nettyProxyMsg.setClientTargetPort(clientTargetPort);
                nettyProxyMsg.setType(ProxyMessageType.SOCKS_REPORT_CLIENT_PROXY_CLIENT_OTHER_TRANSFER_CONNECTION_SUCCESS_);
                transferChannel.writeAndFlush(nettyProxyMsg);

                ChannelAttributeKeyUtils.buildNextChannel(transferChannel, realChannel);
                ChannelAttributeKeyUtils.buildNextChannel(realChannel, transferChannel);

                // 绑定客户端真实通信通道
                ChannelAttributeKeyUtils.buildVisitorId(transferChannel, visitorId);
                ChannelAttributeKeyUtils.buildClientId(transferChannel, clientId);


            } else {
                log.info("无法连接到服务端....");
                eventLoopGroup.schedule(() -> {
                    try {
                        newVisitorConnect2Server(clientId,
                                clientTargetIp,
                                clientTargetPort,
                                visitorId, realChannel, nettyClientProperties, handleChannelTypeAdvancedList);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }, 2, TimeUnit.SECONDS);
            }
        });
    }

}